/*
 * Copyright 2006 Bret Taylor
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

%{

#include "ast.h"
#include "filelocation.h"
#include "log.h"

#include <string>
#include <iostream>
#include <memory>

using namespace indie;
using std::istream;
using std::string;
using std::auto_ptr;

#include "yaccparser.h"

/* Global variables */
static int g_current_line = 0;
static int g_current_column = 0;
static const FileLocationFactory* g_factory = NULL;
static Log* g_log = NULL;
static istream* g_stream = NULL;

/* For calculation of our column numbers */
static const unsigned int kTabSize = 8;

/* Save the location in the appropriate yacc data structure */
extern struct yyltype yylloc;

/* Eecords line/column numbers before every action. */
static void OnBeforeAction();
#define YY_USER_ACTION OnBeforeAction();

/* Read from our stream rather than the default lex file descriptor */
#define YY_INPUT(buffer, result, buffer_size) \
    g_stream->read(buffer, buffer_size); \
    result = g_stream->gcount()

%}

/* Parsing stack */
%option stack
%s I
%s C

/* Definitions to make some of our rules more readable. */
Digit		([0-9])
HexDigit	([0-9a-fA-F])
HexInteger	(0[Xx]{HexDigit}+)
Integer		([-]?{Digit}+)
Exponent	([Ee][-+]?{Integer})
Float		({Integer}"."{Digit}*{Exponent}?)
StringStart	(\"[^"\n]*)
String		({StringStart}\")
Identifier	([_]*[a-zA-Z][a-zA-Z_0-9]*)
Operator	([+\-*/%=<>\\.,;!():\[\]{}])
BeginComment	("/*")
EndComment	("*/")
SingleComment	("//"[^\n]*)

%%

 /* Rules for whitespace */

<*>\n {
    g_current_line++;
    g_current_column = 1;
}

<*>[ ]+ {
    /* Ignore all spaces in normal strings and in comments */
}

<*>[\t] {
    g_current_column += kTabSize - g_current_column % kTabSize + 1;
}


 /* Rules for scanning comments */

<*>{BeginComment} {
    yy_push_state(C);
}

<C>{EndComment} {
    yy_pop_state();
}

<C><<EOF>> {
    /* ERROR */
    return 0;
}

<C>. {
    /* Ignore all text in comments */
}

<I>{SingleComment} {
    /* Ignore comments */
}


 /* Rules for scanning keywords */

<I>"class" {
    return T_Class;
}
<I>"method" {
    return T_Method;
}
<I>"extends" {
    return T_Extends;
}
<I>"while" {
    return T_While;
}
<I>"for" {
    return T_For;
}
<I>"if" {
    return T_If;
}
<I>"return" {
    return T_Return;
}
<I>"break" {
    return T_Break;
}
<I>"new" {
    return T_New;
}
<I>"is" {
    return T_Is;
}
<I>"true" {
    return T_True;
}
<I>"false" {
    return T_False;
}
<I>"this" {
    return T_This;
}


 /* Rules for scanning operators */

<I>"&&" {
    return T_And;
}
<I>"||" {
    return T_Or;
}
<I>"==" {
    return T_Equal;
}
<I>"!=" {
    return T_NotEqual;
}
<I>"<=" {
    return T_LessEqual;
}
<I>">=" {
    return T_GreaterEqual;
}
<I>"++" {
    return T_Increment;
}
<I>"--" {
    return T_Decrement;
}
<I>{Operator} {
    return yytext[0];
}


 /* Rules for scanning string and number constants */

<I>{Integer} {
    int value = strtol(yytext, NULL, 10);
    yylval.IntegerConstant =
	new AstIntegerConstant(g_factory->create(yylloc), value);
    return T_IntegerConstant;
}

<I>{HexInteger} {
    int value = strtol(yytext, NULL, 10);
    yylval.IntegerConstant =
	new AstIntegerConstant(g_factory->create(yylloc), value);
    return T_IntegerConstant;
}

<I>{Float} {
    double value = atof(yytext);
    yylval.FloatConstant =
	new AstFloatConstant(g_factory->create(yylloc), value);
    return T_FloatConstant;
}

<I>{String} {
    string* value = new string(yytext);
    yylval.StringConstant =
	new AstStringConstant(g_factory->create(yylloc), value);
    return T_StringConstant;
}


 /* Identifiers */

<I>{Identifier} {
    yylval.String = new string(yytext);
    return T_Identifier;
}


 /* Error handler */

<I>. {
    auto_ptr<FileLocation> loc(g_factory->create(yylloc));
    g_log->Error(*loc, "Unexpexted character '%c'", yytext[0]);
}


%%


/*
 * Turn off debugging for now. We don't need to provide a real file to
 * yyrestart because our line reader does all the file-reading. We just want
 * to reset Lex's internal data.
 */
void InitializeScanner(const FileLocationFactory& factory, Log& log,
		       istream& stream) {
    g_factory = &factory;
    g_log = &log;
    g_stream = &stream;
    g_current_line = 1;
    g_current_column = 1;

    yy_flex_debug = false;
    yyrestart(NULL);

    /* Start in our normal state */
    BEGIN(I);
}


/*
 * This method is called before each action.
 */
static void OnBeforeAction()
{
    yylloc.first_line = g_current_line;
    yylloc.last_line = g_current_line;
    yylloc.first_column = g_current_column;
    yylloc.last_column = g_current_column + yyleng - 1;
    g_current_column += yyleng;
}
